/******************************************************************************************************
Copyright (C) 2013 Mestrelab Research S.L. All rights reserved.

This file is part of the MNova scripting toolkit.

Authorized users of MNova Software may use this file freely, but this file is provided AS IS
with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE.
*****************************************************************************************************/

/*globals Settings, DBPlugin, GroupBox, LineEdit, SpinBox, CheckBox, RadioButton, ToolButton, Dialog, qsTr, settings, MessageBox, ComboBox, DBDefinition, Application, ProgressDialog, print, dbMakeDBItems, MnUi*/
/*jslint plusplus: true, indent: 4*/

function dbCopyDatabase() {
	"use strict";
	var dialog, chDstSameAsSrc, leDstHost, leSrcHost, leDstPort, leSrcPort, leDstUser, leSrcUser, leDstPassword, leSrcPassword,
		tbSrcConnect, cbSrcDatabase, gbSrcDatabase, tbDstConnect, cbDstDatabase, gbDstDatabase, gbSrcRecords, sbFrom, sbTo,
		gbDstExisting, gbDstNew, gbSrc, gbDst, gbSrcConnection, chSrcSavePassword, gbDstConnection, chDstSavePassword, leDstDatabase,
		rbDefinitionCopy, rbDefinitionDefault, ok, dbSchema, dbName, txtRecords, txtSrcHost, txtDstHost, progress, countRecords,
		countItems, startTime, dstDB, cidSrc, startTimeRecord, dw, dbItems, cidDst,

		srcHostSettingsKey = "Database/Copy Database/Source/Host",
		srcPortSettingsKey = "Database/Copy Database/Source/Port",
		srcUserSettingsKey = "Database/Copy Database/Source/User",
		srcPasswordSettingsKey = "Database/Copy Database/Source/Password",
		srcSavePasswordSettingsKey = "Database/Copy Database/Source/Save Password",
		srcDatabaseSettingsKey = "Database/Copy Database/Source/Database",

		dstHostSettingsKey = "Database/Copy Database/Destination/Host",
		dstPortSettingsKey = "Database/Copy Database/Destination/Port",
		dstUserSettingsKey = "Database/Copy Database/Destination/User",
		dstPasswordSettingsKey = "Database/Copy Database/Destination/Password",
		dstSavePasswordSettingsKey = "Database/Copy Database/Destination/Save Password",

		defaultConnectionSettings = new Settings("DBConnection/"),
		defHost = defaultConnectionSettings.value("Server", "localhost"),
		defPort = defaultConnectionSettings.value("Port", "5504"),
		defUser = defaultConnectionSettings.value("Username", ""),
		defPassword = defaultConnectionSettings.value("Password", ""),

		srcClient = DBPlugin.createClient(),
		dstClient = DBPlugin.createClient(),
		duplicatePolicy = ["Ask", "Add", "Ignore", "Replace"];

	// Helper functions to save typing:
	function createGroupBox(aParent, aTitle) {
		var gb = new GroupBox();
		gb.title = aTitle;
		if (aParent) {
			aParent.add(gb);
		}
		return gb;
	}

	function createLineEdit(aParent, aLabel, aText) {
		var le = new LineEdit();
		le.label = aLabel;
		le.text = aText;
		if (aParent) {
			aParent.add(le);
		}
		return le;
	}

	function createSpinBox(aParent, aLabel) {
		var sb = new SpinBox();
		if (aParent) {
			aParent.add(aLabel, sb);
		}
		return sb;
	}

	function createCheckBox(aParent, aText, aChecked) {
		var cb = new CheckBox();
		cb.text = aText;
		cb.checked = aChecked;
		if (aParent) {
			aParent.add(cb);
		}
		return cb;
	}

	function createRadioButton(aParent, aText, aChecked) {
		var rb = new RadioButton();
		rb.text = aText;
		rb.checked = aChecked;
		if (aParent) {
			aParent.add(rb);
		}
		return rb;
	}

	function createToolButton(aParent, aText) {
		var tb = new ToolButton();
		tb.text = aText;
		if (aParent) {
			aParent.add(tb);
		}
		return tb;
	}

	function populateComboBox(aComboBox, aValues, aPreselected) {
		aComboBox.clear();
		if (aPreselected === "") {
			aComboBox.addItem("");
		}
		var i;
		for (i = 0; i < aValues.length; i++) {
			aComboBox.addItem(aValues[i]);
			if (aValues[i] === aPreselected) {
				aComboBox.currentIndex = i;
			}
		}
	}

	// Dialog for source and destination
	dialog = new Dialog(qsTr("Copy Database"));
	dialog.onDstSameAsSrcToggled = function () {
		if (chDstSameAsSrc.checked) {
			leDstHost.text = leSrcHost.text;
			leDstPort.text = leSrcPort.text;
			leDstUser.text = leSrcUser.text;
			leDstPassword.text = leSrcPassword.text;
		}
		leDstHost.enabled = !chDstSameAsSrc.checked;
		leDstPort.enabled = !chDstSameAsSrc.checked;
		leDstUser.enabled = !chDstSameAsSrc.checked;
		leDstPassword.enabled = !chDstSameAsSrc.checked;
	};

	dialog.onSrcConnectClicked = function () {
		if (tbSrcConnect.text === "Connect") {
			tbSrcConnect.enabled = false;
			srcClient.connect(leSrcHost.text, leSrcPort.text, leSrcUser.text, leSrcPassword.text);
			if (srcClient.connected()) {
				leSrcHost.enabled = false;
				leSrcPort.enabled = false;
				leSrcUser.enabled = false;
				leSrcPassword.enabled = false;
				tbSrcConnect.text = "Disconnect";
				populateComboBox(cbSrcDatabase, srcClient.databases(), settings.value(srcDatabaseSettingsKey, ""));
				gbSrcDatabase.enabled = true;
			} else {
				MessageBox.critical(qsTr("Could not connect to server."));
			}
			tbSrcConnect.enabled = true;
		} else if (tbSrcConnect.text === "Disconnect") {
			srcClient.disconnect();
			leSrcHost.enabled = true;
			leSrcPort.enabled = true;
			leSrcUser.enabled = true;
			leSrcPassword.enabled = true;
			tbSrcConnect.text = "Connect";
			gbSrcDatabase.enabled = false;
		}
	};

	dialog.onDstConnectClicked = function () {
		if (tbDstConnect.text === "Connect") {
			tbDstConnect.enabled = false;
			dstClient.connect(leDstHost.text, leDstPort.text, leDstUser.text, leDstPassword.text);
			if (dstClient.connected()) {
				chDstSameAsSrc.enabled = false;
				leDstHost.enabled = false;
				leDstPort.enabled = false;
				leDstUser.enabled = false;
				leDstPassword.enabled = false;
				tbDstConnect.text = "Disconnect";
				populateComboBox(cbDstDatabase, dstClient.databases(), "");
				gbDstDatabase.enabled = true;
			} else {
				MessageBox.critical(qsTr("Could not connect to server."));
			}
			tbDstConnect.enabled = true;
		} else if (tbDstConnect.text === "Disconnect") {
			dstClient.disconnect();
			chDstSameAsSrc.enabled = true;
			leDstHost.enabled = !chDstSameAsSrc.checked;
			leDstPort.enabled = !chDstSameAsSrc.checked;
			leDstUser.enabled = !chDstSameAsSrc.checked;
			leDstPassword.enabled = !chDstSameAsSrc.checked;
			tbDstConnect.text = "Connect";
			gbDstDatabase.enabled = false;
		}
	};

	dialog.onSrcDatabaseChanged = function (aDatabase) {
		if (aDatabase) {
			var db = srcClient.definition(aDatabase);
			if (db.records > 0) {
				gbSrcRecords.enabled = true;
				sbFrom.minimum = 1;
				sbFrom.maximum = db.records;
				sbFrom.value = 1;
				sbTo.minimum = 1;
				sbTo.maximum = db.records;
				sbTo.value = db.records;
			} else {
				gbSrcRecords.enabled = false;
			}
		} else {
			gbSrcRecords.enabled = false;
		}
		if (!gbSrcRecords.enabled) {
			sbFrom.minimum = 0;
			sbFrom.value = 0;
			sbTo.minimum = 0;
			sbTo.value = 0;
		}
	};

	dialog.onDstNewClicked = function (selected) {
		gbDstExisting.setChecked(!selected);
	};

	dialog.onDstExistingClicked = function (selected) {
		gbDstNew.setChecked(!selected);
	};

	gbSrc = createGroupBox(null, "Source");
	gbDst = createGroupBox(null, "Destination");
	dialog.add(gbSrc, gbDst);

	gbSrcConnection = createGroupBox(gbSrc, "Connection");
	gbSrcConnection.add("");                  // add an empty line 
	leSrcHost = createLineEdit(gbSrcConnection, "Host:\t", settings.value(srcHostSettingsKey, defHost));
	leSrcPort = createLineEdit(gbSrcConnection, "Port:\t", settings.value(srcPortSettingsKey, defPort));
	leSrcUser = createLineEdit(gbSrcConnection, "User:\t", settings.value(srcUserSettingsKey, defUser));
	leSrcPassword = createLineEdit(gbSrcConnection, "Password:\t", settings.value(srcPasswordSettingsKey, defPassword));
	leSrcPassword.echoMode = LineEdit.EchoMode.Password;
	chSrcSavePassword = createCheckBox(null, "Save Password", settings.value(srcSavePasswordSettingsKey, false));
	tbSrcConnect = createToolButton(null, "Connect");
	tbSrcConnect.clicked.connect(dialog, dialog.onSrcConnectClicked);
	gbSrcConnection.add(chSrcSavePassword, tbSrcConnect);

	gbSrcDatabase = createGroupBox(gbSrc, "Database");
	gbSrcDatabase.enabled = false;
	cbSrcDatabase = new ComboBox();
	cbSrcDatabase.editable = false;
	cbSrcDatabase['currentIndexChanged(QString)'].connect(dialog, dialog.onSrcDatabaseChanged);
	gbSrcDatabase.add("Database:\t", cbSrcDatabase);

	gbSrcRecords = createGroupBox(gbSrc, "Records");
	gbSrcRecords.enabled = false;
	sbFrom = createSpinBox(gbSrcRecords, "From:");
	sbTo = createSpinBox(gbSrcRecords, "To:");

	gbDstConnection = createGroupBox(gbDst, "Connection");
	chDstSameAsSrc = createCheckBox(gbDstConnection, "Same as Source", false);
	chDstSameAsSrc.toggled.connect(dialog, dialog.onDstSameAsSrcToggled);
	leDstHost = createLineEdit(gbDstConnection, "Host:\t", settings.value(dstHostSettingsKey, defHost));
	leDstPort = createLineEdit(gbDstConnection, "Port:\t", settings.value(dstPortSettingsKey, defPort));
	leDstUser = createLineEdit(gbDstConnection, "User:\t", settings.value(dstUserSettingsKey, defUser));
	leDstPassword = createLineEdit(gbDstConnection, "Password:\t", settings.value(dstPasswordSettingsKey, defPassword));
	leDstPassword.echoMode = LineEdit.EchoMode.Password;
	chDstSavePassword = createCheckBox(null, "Save Password", settings.value(dstSavePasswordSettingsKey, false));
	tbDstConnect = createToolButton(null, "Connect");
	tbDstConnect.clicked.connect(dialog, dialog.onDstConnectClicked);
	gbDstConnection.add(chDstSavePassword, tbDstConnect);

	gbDstDatabase = createGroupBox(gbDst, "Database");
	gbDstDatabase.enabled = false;

	gbDstExisting = createGroupBox(gbDstDatabase, "Existing Database:");
	gbDstExisting.checkable = true;
	gbDstExisting.checked = true;
	gbDstExisting.clicked.connect(dialog.onDstExistingClicked);
	gbDstExistingGeneral = createGroupBox(gbDstExisting, "");
	cbDstDatabase = new ComboBox();
	cbDstDatabase.editable = false;
	gbDstExistingGeneral.add("Name:", cbDstDatabase);
	gbDstExistingHoldRecIds = createGroupBox(gbDstExisting, "Preserve Record Ids");
	gbDstExistingHoldRecIds.checkable = true;
	cbDstDuplicatePolicy = new ComboBox();
	cbDstDuplicatePolicy.editable = false;
	populateComboBox(cbDstDuplicatePolicy, duplicatePolicy, "ASK");
	gbDstExistingHoldRecIds.add("Duplicate Polcicy:", cbDstDuplicatePolicy);


	gbDstNew = createGroupBox(gbDstDatabase, "New Database:");
	gbDstNew.checkable = true;
	gbDstNew.checked = false;
	gbDstNew.clicked.connect(dialog.onDstNewClicked);
	leDstDatabase = createLineEdit(gbDstNew, "Name:\t", "");
	rbDefinitionCopy = createRadioButton(gbDstNew, "Copy definition from source", true);
	rbDefinitionDefault = createRadioButton(gbDstNew, "Default definition", false);
	chDstNewHoldRecIds = createCheckBox(gbDstNew, "Preserve Record Ids", true);

	ok = false;
	while (!ok && dialog.exec()) {
		if (!srcClient.connected()) {
			MessageBox.critical(qsTr("Source client must be connected to proceed."));
			tbSrcConnect.setFocus();
		} else if (cbSrcDatabase.currentText.length <= 0) {
			MessageBox.critical(qsTr("You must specify a source database."));
			cbSrcDatabase.setFocus();
		} else if (!srcClient.openDatabase(cbSrcDatabase.currentText)) {
			MessageBox.critical(qsTr("Could not open source database '{0}'.").format(cbSrcDatabase.currentText));
			cbSrcDatabase.setFocus();
		} else if (srcClient.definition(srcClient.database).records <= 0) {
			MessageBox.critical(qsTr("Source database '{0}' is empty.").format(srcClient.database));
			cbSrcDatabase.setFocus();
		} else if (sbFrom.value > sbTo.value) {
			MessageBox.critical(qsTr("'To Record' must be greater than or equal to 'From Record'."));
			sbFrom.setFocus();
		} else if (!dstClient.connected()) {
			MessageBox.critical(qsTr("Destination client must be connected to proceed."));
			tbDstConnect.setFocus();
		} else if (gbDstExisting.checked) {
			if (cbDstDatabase.currentText.length <= 0) {
				MessageBox.critical(qsTr("You must specify a destination database."));
				cbDstDatabase.setFocus();
			} else if (leSrcHost.text === leDstHost.text && leSrcPort.text === leDstPort.text && cbDstDatabase.currentText === srcClient.database) {
				MessageBox.critical(qsTr("Destination database must be different from source database."));
				cbDstDatabase.setFocus();
			} else if (!dstClient.openDatabase(cbDstDatabase.currentText)) {
				MessageBox.critical(qsTr("Could not open destination database '{0}'.").format(cbDstDatabase.currentText));
				cbDstDatabase.setFocus();
			} else {
				ok = true;
			}
		} else if (gbDstNew.checked) {
			dbName = leDstDatabase.text.replace(/^\s*/, "").replace(/\s*$/, "");
			dbSchema = rbDefinitionCopy.checked ? srcClient.definition() : DBDefinition.defaultDefinition(dbName);
			dbSchema.name = dbName;
			if (dbName.length <= 0) {
				MessageBox.critical(qsTr("You must specify a destination database."));
				leDstDatabase.setFocus();
			} else if (cbDstDatabase.items.indexOf(dbName) !== -1) {
				MessageBox.critical(qsTr("A database called '{0}' already exists.").format(dbName));
				leDstDatabase.setFocus();
			} else if (!dstClient.createDatabase(dbSchema)) {
				MessageBox.critical(qsTr("Could not create destination database '{0}'.").format(dbName));
				leDstDatabase.setFocus();
			} else if (!dstClient.openDatabase(dbName)) {
				MessageBox.critical(qsTr("Could not open destination database '{0}'.").format(dbName));
				leDstDatabase.setFocus();
			} else {
				ok = true;
			}
		}
		txtRecords = sbTo.value - sbFrom.value > 1 ? "records " + sbFrom.value + ".." + sbTo.value : "record " + sbFrom.value;
		txtSrcHost = " at " + leSrcHost.text + ":" + leSrcPort.text;
		txtDstHost = leDstHost.text !== leSrcHost.text || leDstPort.text !== leSrcPort.text ? " at " + leDstHost.text + ":" + leDstPort.text : "";
		ok = ok && (MessageBox.question(qsTr("Copy {0}\nfrom {1}{2}\nto {3}{4}?\n").format(txtRecords, srcClient.database, txtSrcHost, dstClient.database, txtDstHost),
                                        MessageBox.Yes, MessageBox.Cancel) === MessageBox.Yes);
	}
	if (!ok) {
		return;
	}

// Save settings
	settings.setValue(srcHostSettingsKey, leSrcHost.text);
	settings.setValue(srcPortSettingsKey, leSrcPort.text);
	settings.setValue(srcUserSettingsKey, leSrcUser.text);
	settings.setValue(srcPasswordSettingsKey, chSrcSavePassword.checked ? leSrcPassword.text : "");
	settings.setValue(srcSavePasswordSettingsKey, chSrcSavePassword.checked);
	settings.setValue(srcDatabaseSettingsKey, srcClient.database);
	settings.setValue(dstHostSettingsKey, leDstHost.text);
	settings.setValue(dstPortSettingsKey, leDstPort.text);
	settings.setValue(dstUserSettingsKey, leDstUser.text);
	settings.setValue(dstPasswordSettingsKey, chDstSavePassword.checked ? leDstPassword.text : "");
	settings.setValue(dstSavePasswordSettingsKey, chDstSavePassword.checked);

	progress = new ProgressDialog();
	progress.setRange(sbFrom.value, sbTo.value + 1);
	progress.show();

	// Now do the copying
	countRecords = 0;
	countItems = 0;
	startTime = new Date();
	holdRecIds = (gbDstExisting.checked && gbDstExistingHoldRecIds.checked) || (gbDstNew.checked && chDstNewHoldRecIds.checked);
	print("It is now " + startTime.toString());
	try {
		dstDB = dstClient.definition(dstClient.database);
		for (cidSrc = sbFrom.value; cidSrc <= sbTo.value && !progress.wasCanceled; ++cidSrc) {
			startTimeRecord = new Date();
			progress.value = cidSrc;
			progress.labelText = qsTr("Copying record {0} ({1} of {2})...").format(cidSrc, cidSrc - sbFrom.value + 1, sbTo.value - sbFrom.value + 1);
			Application.mainWindow.processEvents();

			// Retrieve record and paste into new document
			dw = Application.mainWindow.newDocument();
			print("Retrieving record " + cidSrc + "...");
			DBPlugin.getRecord(srcClient, cidSrc);
			print("Pasted " + dw.itemCount() + " items from record id " + cidSrc + ".");
			dbItems = dbMakeDBItems(dstClient, dstDB, true, false);
			if (dbItems.length > 0) {
				// Save document
				cidDst = dstClient.saveRecord(holdRecIds ? cidSrc : 0, dbItems, cbDstDuplicatePolicy.currentIndex, true); 
				if (cidDst > 0) {
					print("Copied " +  dbItems.length + " items to record " + cidDst + " in " + ((new Date() - startTimeRecord) / 1000) + " seconds.");
					countRecords++;
					countItems += dbItems.length;
				} else {
					MessageBox.warning(qsTr("Error while storing source record {0}.").format(cidSrc));
				}
			} else {
				MessageBox.warning("Could not find object data for record {0} in database {1}.".replace('{0}', cidSrc).replace('{1}', srcClient.database));
			}
			dw.close();
		}
	} finally {
		progress.close();
	}
	print("All done");
	srcClient.disconnect();
	dstClient.disconnect();
	MessageBox.information(qsTr("Copied {0} items in {1} records.").format(countItems, countRecords));
}

if (this.MnUi && MnUi.scripts_db) {
	MnUi.scripts_db.scripts_db_CopyDatabase = dbCopyDatabase;
}
